import c3d
from OOSwarm.CarraraObject import CarraraObject


PARM_ABSOLUTE_DISTANCE = 0
PARM_ON_OFF = 1
PARM_ANGLE = 2
PARM_STRING = 3

#   SceneParser : scan the object tree in Carrara in order to find all controls 
#                 and their related object


class SceneParser(object):
  def __init__(self,controllers=None):
    super(SceneParser,self).__init__()
    self.objects = []
    self.controllers = controllers
    
#   get the object direct children in the tree. If object is None, start from the scene
  def get_children(self,object=None):
    if object is None:
      l = []
      names = []
      temp_list=c3d.objects_list()
      for o in temp_list:
        no = o.split("/")
        if len(no) == 2:
          if o in names:
            print "name",o,"not unique in first level -> ignored"
          else:
            obj = c3d.Object("/" + no[1])
            l.append(obj)
            names.append(o)
      return l
    objl = []
    i = 1
    running = True
    while running:
      try:
        o = object.walk("/#%d" % i )
        i += 1
        objl.append(o)
      except ValueError:
        running = False
    return objl
    
#   get_name : helper method to extract the last name in the path
  def get_name(self,obj):
    if obj is None:
      return "None"
    return obj.get_fullname().split("/").pop()
  
#   Build the tree in memory, will be scanned later
  def build_tree(self,obj=None):
    l = self.get_children(obj)
    ret = []
    for o in l:
      l1 = self.build_tree(o)
      ret.append((o,l1))
    if obj is None:
      self.objects = ret
    return ret  

#   extract the controllers in the given object list (direct children of obj) and call recusively for objects not controls
  def get_controllers(self,cont_list,obj=None,obj_list=None):
    ret = []
    if obj_list is None:
      l = self.objects
    else:
      l = obj_list
    for (el,li) in l:
      found = False
      for c in cont_list:
        if c.is_name(self.get_name(el)):
          ret.append(c.get_control_instance(obj,el,self.get_parameters(c.parm_list,el,li)))
          found=True
          break
      if found:
        break
      ret += self.get_controllers(cont_list,obj=el,obj_list=li)
    return ret
    
#   get the paramters for a given control
  def get_parameters(self,parm_list,obj=None,obj_list=None):
    ret = []
    l = obj_list
    for (el,li) in l:
      found = False
      for p in parm_list:
        if p.is_name(self.get_name(el)):
          ret.append(p.get_parm_instance(el))
          found=True
          break
      ret += self.get_parameters(parm_list,obj=el,obj_list=li)
    return ret
          
    
# Marker : superclass for all markers in Carrara (control, parameters)    
    
class Marker(object):
  def __init__(self,name):
    super(Marker,self).__init__()
    self.name = name
  def is_name(self,n):
    return n == "&" + self.name
    

# Parameter : description of a parameter to be searched for in the object tree

class Parameter(Marker):
  
  def __init__(self,name,typ,attribute=None):
    super(Parameter,self).__init__(name=name)
    self.typ = typ
    self.attribute = attribute
    if attribute is None:
      self.attribute= name
#   factory method : return a Parm class associated with the description
  def get_parm_instance(self,object):
    carrara_object = CarraraObject(obj=object,mode="L")
    if self.typ == PARM_ON_OFF:
      return ParmOnOff(object=carrara_object,parameter=self) 
    elif self.typ == PARM_ABSOLUTE_DISTANCE:
      return ParmDistance(object=carrara_object,parameter=self)
    else:
      print "type",self.typ,"unknown" 

    

# Parameter : description of a control to be searched for in the object tree
class Controller(Marker):
  def __init__(self,name,parm_list=[]):
    super(Controller,self).__init__(name=name)
    self.parm_list = parm_list
  def is_name(self,n):
    return n == "&" + self.name
#   factory method : return a Control class associated with the description
  def get_control_instance(self,carrara_object,object,parameters):
    if carrara_object is None:
       carrara_object_inst = None
    else:
      carrara_object_inst = CarraraObject(obj=carrara_object)
    object_inst = CarraraObject(obj=object,mode="L")
    
    if self.name == "Simulation":
      return SimulationControl(carrara_object=carrara_object_inst,object=object_inst,controller=self,parameters=parameters) 
    elif self.name == "Boid":
      return BoidControl(carrara_object=carrara_object_inst,object=object_inst,controller=self,parameters=parameters) 
    

# Parm : instance of a parameter in the object tree
class Parm(object):
  def __init__(self,object,parameter):
    super(Parm,self).__init__()
    self.object = object
    self.parameter = parameter
    self.name = self.parameter.name
    self.attribute = self.parameter.attribute

  def get_value(self,time):
    raise NotImplementedError()

  def set_value_time(self,time):
    t = c3d.get_time()
    if not (t == time):
      c3d.set_time(time)
 

# ParmOnOff : indicate a boolean value accordiong to the rotation in the Y axis (0: False, 90: True)

class ParmOnOff(Parm):
  def __init__(self,**args):
    super(ParmOnOff,self).__init__(**args)
  def get_value(self,time):
    self.set_value_time(time)
    self.object.refresh()
    v = self.object.orientation.y
    return (abs(90.0 - v)<= 0.1)

# ParmDistance : indicate a distance (translation on z axis)

class ParmDistance(Parm):
  def __init__(self,**args):
    super(ParmDistance,self).__init__(**args)
  def get_value(self,time):
    self.set_value_time(time)
    self.object.refresh()
    return self.object.position.z
     
# Control : instance of a controller in the object tree
class Control(object):
  def __init__(self,carrara_object,object,controller,parameters):
    super(Control,self).__init__()
    self.carrara_object = carrara_object
    self.controller = controller
    self.object = object
    self.parameters = parameters
    self.fps = 24.0
  def get_parameter_value(self,parm,time):
    return parm.get_value(time)
  def get_parameter(self,attribute):
    for p in self.parameters:
      if p.attribute == attribute:
        return p
    return None
    
#     
# SimulationControl : get the parameters for the simulation
# 
class SimulationControl(Control):
  def __init__(self,**args):
    super(SimulationControl,self).__init__(**args)

  def get_boundaries(self):
    time = 0.0
    p = self.get_parameter("Running")
    on = p.get_value(time)
    while (not on and time < 9.0):
      time += 1.0/self.fps
      on = p.get_value(time)
    if (time < 999.0):
      start = time
    else:
      start = 0.0
    while (on and time < 9.0):
      time += 1.0/self.fps
      on = p.get_value(time)
    end = time
    return (start,end)

      
    
# SimulationControl : get the parameters for the simulation
    

class BoidControl(Control):
  def __init__(self,**args):
    super(BoidControl,self).__init__(**args)

# Controls : helper class to retrieve extracted controls
# use dictionnaries for quick access

class Controls(object):
  def __init__(self,control_list):
    self.control_list = control_list
    self.control_class_dict = {}
    for e in self.control_list:
      c = e.__class__
      if c in self.control_class_dict:
        self.control_class_dict[c].append(e)
      else:
        self.control_class_dict[c] = [e]
      
  def get_subset(self,control_class=None):
    result = []
    if control_class is not None:
      if control_class in self.control_class_dict:
        result += self.control_class_dict[control_class]
    return result

  def get_first(self,**args):
    r = self.get_subset(**args)
    return r[0]if len(r)>0  else None
  


